package com.winning.gjhlht.ihe.common;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;

import com.winning.common.tools.XMLTool;

/**
 * 验证CDA文档非空
 *
 * @author liuwensheng
 */
public class ValidateCDA {
    /**
     * 根据模板配置的搜索节点是否存在的配置，来对已经生成的CDA文档XML结构进行验证
     *
     * @param cdaDocument
     * @param findConditionStr
     * @return
     */
    public boolean findNodesFromDoc(Document cdaDocument, String findConditionStr) {
        boolean result = false;

        if (null != cdaDocument) {
            Element rootElement = cdaDocument.getRootElement();

            if (null != rootElement) {
                String[] conditionStrList = findConditionStr.split(",");

                for (int index = 0; conditionStrList != null && index < conditionStrList.length; index++) {
                    String conditionStr = conditionStrList[index] == null ? "" : conditionStrList[index];
                    conditionStr = conditionStr.replaceAll(" ", "");

                    //拼装XPath条件搜索节点, 需要代自定义的命名空间
                    String xpath = "//" + IHEConstans.CDA_XMLNS + ":" + conditionStr;

                    List<Element> resultList = cdaDocument.selectNodes(xpath);

                    if (null == resultList || resultList.size() == 0) {
                        result = false;
                        break;
                    } else {
                        result = true;
                    }
                }
            }
        }

        return result;
    }


    public Document updateCDAXMLForDocument(Document cdaDocument, String cdaFileName) {


        Document document = cdaDocument;//cdaConfig.getDocument("Test.xml");

        try {

            //搜索CDA文档结构中存在result属性的所有Attribute，不管节点在XML文档中的具体位置
            //String xpath = "//@result";
            List<Attribute> list = document.selectNodes(IHEConstans.XPATH_VALUE);

            Iterator<Attribute> iter = list.iterator();

            //1、遍历所有的属性对象
            while (iter.hasNext()) {
                Attribute attribute = (Attribute) iter.next();

                String resultStr = attribute.getStringValue() == null ? "" : attribute.getStringValue();

                //如果当前result属性值为error，则需要利用递归算法，推算出父节点的异常情况，根据节点的约束条件综合判断
                if (IHEConstans.ERROR_VALUE.equals(resultStr)) {
                    //当前属性值所在的节点对象
                    Element currentAttributeElement = attribute.getParent();

                    diguiSetResult(currentAttributeElement);
                }
            }


            //3、删除存在异常的节点
            document = deleteErrorElementForDocument(document);


            //4、去掉没有作用的约束条件属性
            document = deleteRestraintAttributeForDocument(document);


            //2、将中间CDA 文档写入独立文件夹下，以备错误定位分析用
            //XMLTool.writeCDAtransitionFile(document, cdaFileName);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return document;
    }


    public Document deleteErrorElementForDocument(Document document) {

        String errorElementXPath = "//*[@result='error']";

        if (null != document) {
            List<Element> errorElement = document.selectNodes(errorElementXPath);

            for (Iterator iterator = errorElement.iterator(); iterator.hasNext(); ) {
                Element element = (Element) iterator.next();

                Element parentElement = element.getParent();

                if (null != parentElement) {
                    parentElement.remove(element);
                }
            }
        }

        return document;
    }

    public Document deleteRestraintAttributeForDocument(Document document) {

        String restraintAttrXPath = "//@restraint";

        if (null != document) {
            List<Attribute> list = document.selectNodes(restraintAttrXPath);

            Iterator<Attribute> iter = list.iterator();

            //1、遍历所有的属性对象
            while (iter.hasNext()) {
                Attribute attribute = (Attribute) iter.next();

                //当前属性值所在的节点对象
                Element currentAttributeElement = attribute.getParent();

                currentAttributeElement.remove(attribute);
            }
        }

        return document;
    }


    /**
     * 非空验证及更新CDA文档结构（删除异常的节点）：将存在异常error的节点删除，异常根据配置中参数的非空约束获得
     * 依据：节点的异常是根据参数的非空值，动态给CDA文档添加的标记属性
     *
     * @param cdaDocument
     * @param cdaFileName
     */
    public List<String> updateCDAXML(Document cdaDocument, String cdaFileName) {
        List<String> errorElementStrlist = new ArrayList<String>();

        try {
            Document document = cdaDocument;//cdaConfig.getDocument("Test.xml");

            //搜索CDA文档结构中存在result属性的所有Attribute，不管节点在XML文档中的具体位置
            //String xpath = "//@result";
            List<Attribute> list = document.selectNodes(IHEConstans.XPATH_VALUE);

            Iterator<Attribute> iter = list.iterator();

            //1、遍历所有的属性对象
            while (iter.hasNext()) {
                Attribute attribute = (Attribute) iter.next();

                String resultStr = attribute.getStringValue() == null ? "" : attribute.getStringValue();

                //如果当前result属性值为error，则需要利用递归算法，推算出父节点的异常情况，根据节点的约束条件综合判断
                if (IHEConstans.ERROR_VALUE.equals(resultStr)) {
                    //当前属性值所在的节点对象
                    Element currentAttributeElement = attribute.getParent();

                    diguiSetResult(currentAttributeElement);
                }
            }


            //3、删除存在异常的节点
            errorElementStrlist = deleteErrorElement(document);

            //4、去掉没有作用的约束条件属性
            deleteRestraintAttribute(document);

            //2、将中间CDA 文档写入独立文件夹下，以备错误定位分析用
            XMLTool.writeCDAtransitionFile(cdaDocument, cdaFileName);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return errorElementStrlist;
    }

    /**
     * 利用递归算法，由属性值为error的节点开始，从XML内部向外部判断父节点的异常
     * 判断依据：依据节点的约束性 1..1; 1..*; 0..1; 0..*
     *
     * @param currentElement
     */
    private void diguiSetResult(Element currentElement) {
        String childElementName = currentElement.getName();

        //获取当前节点的约束条件，依据节点异常信息，来推断父节点的异常情况
        String restraintStr = currentElement.attributeValue(IHEConstans.RESTRAINT_TEMP) == null ? "" : currentElement.attributeValue(IHEConstans.RESTRAINT_TEMP);

        Element parentElement = currentElement.getParent();

        if (null == parentElement) {
            return;
        }

        //判断是否已经到达根节点，如果是根节点则不需要给根节点添加异常
        if (parentElement.isRootElement()) {
            return;
        }

        //最多一个节点
        if ("1..1".equals(restraintStr)) {
            parentElement.setAttributeValue(IHEConstans.RESULT_TEMP, IHEConstans.ERROR_VALUE);
        }
        //如果约束条件为至少一条大于任一条时，如果实际节点条数小于2条，则父节点也应该是异常
        else if ("1..*".equals(restraintStr)) {
            //从父节点取出相同节点名称的子节点的列表,判断是否存在多个约束的子节点
            List<Element> currentElementList = parentElement.elements(childElementName);

            //取子节点的异常情况
            boolean isError = getElementResult(currentElementList);

            if (isError) {
                parentElement.setAttributeValue(IHEConstans.RESULT_TEMP, IHEConstans.ERROR_VALUE);
            } else {
                //如果多选节点不全是异常节点，则递归终止
                return;
            }
        } else if ("0..1".equals(restraintStr) || "0..*".equals(restraintStr)) {
            //如果当前节点是可选项，对父节点不产生影响,则递归终止
            return;
        } else if ("".equals(restraintStr)) {
            //如果节点没有添加约束条件，则认为是空目录，起到层级作用，需要设置异常属性，作为判断父节点异常的依据
            parentElement.setAttributeValue(IHEConstans.RESULT_TEMP, IHEConstans.ERROR_VALUE);
        }

        //开始递归向上推父节点的异常状况
        diguiSetResult(parentElement);
    }

    /**
     * 通用查询XML节点的异常属性result的值是否为error
     *
     * @param currentElementList
     * @return
     */
    private boolean getElementResult(List<Element> currentElementList) {
        boolean isError = true;

        for (Iterator iterator = currentElementList.iterator(); iterator.hasNext(); ) {
            Element element = (Element) iterator.next();

            String errorStr = element.attributeValue(IHEConstans.RESULT_TEMP);

            //只要存在一个节点没有异常，则全正常(在多节点的情况下)
            if (!IHEConstans.ERROR_VALUE.equals(errorStr)) {
                isError = false;
                break;
            }
        }

        return isError;
    }

    /**
     * 根据节点属性值为error，利用xpath搜索到所有符合条件的节点，并全部删除
     *
     * @param document
     */
    private List<String> deleteErrorElement(Document document) {
        List<String> errorElementStrlist = new ArrayList<String>();

        String errorElementXPath = "//*[@result='error']";

        if (null != document) {
            List<Element> errorElement = document.selectNodes(errorElementXPath);

            for (Iterator iterator = errorElement.iterator(); iterator.hasNext(); ) {
                Element element = (Element) iterator.next();

                //返回存在异常的节点字符串
                errorElementStrlist.add(element.asXML());

                Element parentElement = element.getParent();

                if (null != parentElement) {
                    parentElement.remove(element);
                }
            }
        }

        return errorElementStrlist;
    }

    /**
     * 将生成的CDA文档中作为约束条件判断的属性去除
     *
     * @param document
     */
    private void deleteRestraintAttribute(Document document) {
        String restraintAttrXPath = "//@restraint";

        if (null != document) {
            List<Attribute> list = document.selectNodes(restraintAttrXPath);

            Iterator<Attribute> iter = list.iterator();

            //1、遍历所有的属性对象
            while (iter.hasNext()) {
                Attribute attribute = (Attribute) iter.next();

                //当前属性值所在的节点对象
                Element currentAttributeElement = attribute.getParent();

                currentAttributeElement.remove(attribute);
            }
        }
    }

}
